先上一个例子,显示一个特别长的字符串:
body: Row(
children: <Widget>[
Text(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
style: TextStyle(backgroundColor: Colors.cyan),
),
],
),
显示效果如下:
可以看到在最右边显示出错了,这是因为你要显示的内容超出了可显示的范围,Row 和 Column 都会出现这样的问题,当长度超出时,不会自动折行,在 Flutter 中,提供了 Wrap
和 Flow
来支持这种情况,称之为 “流式布局”。
Wrap
把上面例子中的 Row
改为 Wrap
之后看效果:
可以看到自动折行了,看一下属性:
Wrap({
Key key,
this.direction = Axis.horizontal, //子元素是垂直分布还是水平分布
this.alignment = WrapAlignment.start, //主轴方向上的对齐方式,默认为 start。
this.spacing = 0.0, //主轴方向上的间距。
this.runAlignment = WrapAlignment.start, //主轴方向相对的轴的对齐方式
this.runSpacing = 0.0, //主轴方向相对的轴的间距
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
})
属性这里注释都写的很清楚了,这里就不多做解释了。
Flow
Flow官方介绍是一个对 child 尺寸以及位置调整非常高效的控件,主要是得益于其 FlowDelegate
。另外 Flow 在用转换矩阵 transformation matrices
对 child 进行位置调整的时候进行了优化。
Flow 以及其 child 的一些约束都会受到 FlowDelegate
的控制,例如重写 FlowDelegate
中的 geiSize
,可以设置 Flow 的尺寸,重写其 getConstraintsForChild
方法,可以设置每个 child 的布局约束条件。
Flow 之所以高效,是因为其在定位过后,如果使用 FlowDelegate
中的 paintChildren
改变 child 的尺寸或者位置,只是重绘,并没有实际调整其位置。
例如:
const width = 80.0;
const height = 60.0;
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: Flow(
delegate: TestFlowDelegate(margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0)),
children: <Widget>[
new Container(width: width, height: height, color: Colors.yellow,),
new Container(width: width, height: height, color: Colors.green,),
new Container(width: width, height: height, color: Colors.red,),
new Container(width: width, height: height, color: Colors.black,),
new Container(width: width, height: height, color: Colors.blue,),
new Container(width: width, height: height, color: Colors.lightGreenAccent,),
],
),
);
}
}
class TestFlowDelegate extends FlowDelegate {
EdgeInsets margin = EdgeInsets.zero;
TestFlowDelegate({this.margin});
@override
void paintChildren(FlowPaintingContext context) {
var x = margin.left;
var y = margin.top;
for (int i = 0; i < context.childCount; i++) {
var w = context.getChildSize(i).width + x + margin.right;
if (w < context.size.width) {
context.paintChild(i,
transform: new Matrix4.translationValues(
x, y, 0.0));
x = w + margin.left;
} else {
x = margin.left;
y += context.getChildSize(i).height + margin.top + margin.bottom;
context.paintChild(i,
transform: new Matrix4.translationValues(
x, y, 0.0));
x += context.getChildSize(i).width + margin.left + margin.right;
}
}
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}
样例其实并不复杂,FlowDelegate
需要自己实现 child 的绘制,其实大多数时候就是位置的摆放。上面例子中,对每个 child 按照给定的 margin 值,进行排列,如果超出一行,则在下一行进行布局。
另外,对这个例子多做一个说明,对于上述 child 宽度的变化,这个例子是没问题的,如果每个 child 的高度不同,则需要对代码进行调整,具体的调整是换行的时候,需要根据上一行的最大高度来确定下一行的起始y坐标。